﻿using Dubbo.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Dubbo.Rpc
{
    public class URL
    {
        public string Protocol { get; private set; }

        public string Username { get; private set; }

        public string Password { get; private set; }

        public string Host { get; private set; }

        public int Port { get; private set; }

        public string Path { get; private set; }

        public Dictionary<string, string> Parameters { get; private set; }

        public string IP
        {
            get
            {
                return GetIPByHost(Host);
            }
        }

        public string Address
        {
            get
            {
                return Port <= 0 ? Host : Host + ":" + Port;
            }
        }

        protected URL()
        {
        }

        public URL(string protocol, IPEndPoint endpoint)
            : this(protocol, endpoint.Address.ToString(), endpoint.Port)
        {
        }

        public URL(string protocol, string host, int port)
            : this(protocol, null, null, host, port, null, (Dictionary<string, string>)null)
        {
        }

        public URL(string protocol, string host, int port, Dictionary<string, string> parameters)
            : this(protocol, null, null, host, port, null, parameters)
        {
        }

        public URL(string protocol, string host, int port, string path)
            : this(protocol, null, null, host, port, path, (Dictionary<string, string>)null)
        {
        }

        public URL(string protocol, string host, int port, string path, Dictionary<string, string> parameters)
            : this(protocol, null, null, host, port, path, parameters)
        {
        }

        public URL(string protocol, string username, string password, string host, int port, string path)
            : this(protocol, username, password, host, port, path, (Dictionary<string, string>)null)
        {
        }

        public URL(string protocol, string username, string password, string host, int port, string path, Dictionary<string, string> parameters)
        {
            if (string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
            {
                throw new ArgumentException("Invalid url, password without username!");
            }
            Protocol = protocol;
            Username = username;
            Password = password;
            Host = host;
            Port = (port < 0 ? 0 : port);
            Path = path;
            // trim the beginning "/"
            while (path != null && path.StartsWith("/"))
            {
                path = path.Substring(1);
            }
            if (Parameters == null)
            {
                Parameters = new Dictionary<string, string>();
            }
            else
            {
                Parameters = new Dictionary<string, string>(parameters);
            }
            //this.parameters = Collections.unmodifiableDictionary(parameters);
        }

        public URL SetPath(string path)
        {
            return new URL(Protocol, Username, Password, Host, Port, path, Parameters);
        }

        public string GetParameter(string key)
        {
            string value;
            Parameters.TryGetValue(key, out value);
            if (string.IsNullOrEmpty(value))
            {
                Parameters.TryGetValue(Constants.DEFAULT_KEY_PREFIX + key, out value);
            }
            return value;
        }

        /**
         * Parse url string
         * 
         * @param url URL string
         * @return URL instance
         * @see URL
     */
        public static URL ValueOf(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new ArgumentException("url is invalid");
            }
            string protocol = null;
            string username = null;
            string password = null;
            string host = null;
            int port = 0;
            string path = null;
            Dictionary<string, string> parameters = null;
            int i = url.IndexOf("?"); // seperator between body and parameters 
            if (i >= 0)
            {
                string[] parts = url.Substring(i + 1).Split('&');
                parameters = new Dictionary<string, string>();
                foreach (string p in parts)
                {
                    var part = p.Trim();
                    if (part != string.Empty)
                    {
                        int j = part.IndexOf('=');
                        if (j >= 0)
                        {
                            parameters[part.Substring(0, j)] =  part.Substring(j + 1);
                        }
                        else
                        {
                            parameters[part] = part;
                        }
                    }
                }
                url = url.Substring(0, i);
            }
            i = url.IndexOf("://");
            if (i >= 0)
            {
                if (i == 0)
                {
                    throw new ArgumentException("url missing protocol: \"" + url + "\"");
                }
                protocol = url.Substring(0, i);
                url = url.Substring(i + 3);
            }
            else
            {
                // case: file:/path/to/file.txt
                i = url.IndexOf(":/");
                if (i >= 0)
                {
                    if (i == 0)
                    {
                        throw new ArgumentException("url missing protocol: \"" + url + "\"");
                    }
                    protocol = url.Substring(0, i);
                    url = url.Substring(i + 1);
                }
            }

            i = url.IndexOf("/");
            if (i >= 0)
            {
                path = url.Substring(i + 1);
                url = url.Substring(0, i);
            }

            i = url.IndexOf("@");
            if (i >= 0)
            {
                username = url.Substring(0, i);
                int j = username.IndexOf(":");
                if (j >= 0)
                {
                    password = username.Substring(j + 1);
                    username = username.Substring(0, j);
                }
                url = url.Substring(i + 1);
            }

            i = url.IndexOf(":");
            if (i >= 0 && i < url.Length - 1)
            {
                port = int.Parse(url.Substring(i + 1));
                url = url.Substring(0, i);
            }
            if (url.Length > 0)
            {
                host = url;
            }
            return new URL(protocol, username, password, host, port, path, parameters);
        }

        public static string GetIPByHost(string host)
        {
            try
            {
                IPAddress[] ips = Dns.GetHostAddresses(host);
                return ips[0].ToString();
            }
            catch
            {
                return host;
            }
        }

        /**
	     * Add parameters to a new url.
	     * 
	     * @param parameters
	     * @return A new URL 
	     */
        public URL AddParameters(Dictionary<string, string> parameters)
        {
            if (parameters == null || parameters.Count == 0)
            {
                return this;
            }

            bool hasAndEqual = true;
            foreach (var entry in parameters)
            {
                string value = Parameters[entry.Key];
                if (value == null && entry.Value != null || value != entry.Value)
                {
                    hasAndEqual = false;
                    break;
                }
            }
            // 如果没有修改，直接返回。
            if (hasAndEqual) return this;

            var dict = new Dictionary<string, string>(Parameters);
            foreach(var kvp in parameters)
            {
                dict.Add(kvp.Key, kvp.Value);
            }
            return new URL(Protocol, Username, Password, Host, Port, Path, dict);
        }
    }
}
